/*******************************************************************************
 File Name        : freyabasedata.cpp
 Summary          : Base class of freya data
 Create           : 2020-09-12
 Author           : Schindleren
 Update history   : (Look up at freyabaseaction.h)
*******************************************************************************/
#include "freyabasedata.h"

FreyaBaseData::FreyaBaseData()
{
    dataID = QUuid::createUuid().toString();
    command = 0x0;
    property = QVariantMap();
    pArguments = nullptr;
}

FreyaBaseData::FreyaBaseData(const FreyaBaseData &other)
{
    m_lockProperty.lockForWrite();
    dataID = other.dataID;
    command = other.command;
    property = other.property;
    if(other.pArguments)
    {
        property.insert(FREYALIB_FLG_ARGS, other.pArguments->ToVariant());
    }
    m_lockProperty.unlock();
}

FreyaBaseData::~FreyaBaseData()
{
    DestoryArguments();
}

bool FreyaBaseData::DestoryArguments()
{
    if(pArguments)
    {
        delete pArguments;
        pArguments = nullptr;
    }
    return true;
}

FreyaAbstractStruct *FreyaBaseData::GetArguments() const
{
    return pArguments;
}

QVariant FreyaBaseData::GetProperty()
{
    return GetProperty(FREYALIB_FLG_PROPERTY);
}

void FreyaBaseData::SetProperty(const QVariant &value)
{
    SetProperty(FREYALIB_FLG_PROPERTY, value);
}

QVariant FreyaBaseData::GetProperty(const QString &key, const QVariant &defaultValue)
{
    m_lockProperty.lockForRead();
    QVariant var = property.value(key, defaultValue);
    m_lockProperty.unlock();
    return var;
}

void FreyaBaseData::SetProperty(const QString &key, const QVariant &value)
{
    m_lockProperty.lockForWrite();
    property.insert(key, value);
    m_lockProperty.unlock();
}

bool FreyaBaseData::IsLocalData()
{
    return GetProperty(FREYALIB_FLG_LOCAL, false).toBool();
}

void FreyaBaseData::SetLocalData(bool bLocal)
{
    SetProperty(FREYALIB_FLG_LOCAL, bLocal);
}

qint64 FreyaBaseData::DataDelayTime()
{
    QVariant &&var = GetProperty(FREYALIB_FLG_TIMESTAMP);
    if(!var.isNull())
    {
        return var.toDateTime().msecsTo(QDateTime::currentDateTime());
    }
    return -1;
}

QByteArray FreyaBaseData::Serialize()
{
    QByteArray ba;
    m_lockProperty.lockForWrite();
    QDataStream stream(&ba, QIODeviceBase::WriteOnly);
    QVariantMap varMap;
    varMap.insert(FREYALIB_TYP_CMD, command);
    if(pArguments)
    {
        property.insert(FREYALIB_FLG_ARGS, pArguments->ToVariant());
    }

    QVariant varRequest = property.take(FREYALIB_FLG_REQUESTER);
    varMap.insert(FREYALIB_TYP_ALLVALUE, property);
    stream << dataID << varMap;

    property.insert(FREYALIB_FLG_REQUESTER, varRequest);

    m_lockProperty.unlock();
    return ba;
}

bool FreyaBaseData::Unserialize(const QByteArray &ba)
{
    QString dataId;
    QDataStream stream(ba);
    QVariantMap varMap;
    stream >> dataId >> varMap;
    if(dataId.isEmpty() || varMap.isEmpty())
    {
        return false;
    }
    else
    {
        m_lockProperty.lockForWrite();

        command = varMap.value(FREYALIB_TYP_CMD).toULongLong();
        dataID = dataId;
        property = varMap.value(FREYALIB_TYP_ALLVALUE).toMap();

        m_lockProperty.unlock();
        return true;
    }
}

FreyaData FreyaBaseData::CreateData(const quint64 &cmd)
{
    FreyaData pDate(new FreyaBaseData());
//    qDebug() << "FreyaLib > " << "Create freya data. ID: " << pDate->dataID;
    pDate->command = cmd;
    return pDate;
}

FreyaData FreyaBaseData::CreateData(const FreyaBaseData &other)
{
    FreyaData pDate(new FreyaBaseData(other));
//    qDebug() << "FreyaLib > " << "Create freya data. ID: " << pDate->dataID;
    return pDate;
}

///////
//序列化和反序列化任务组
///////
SerializeTask::SerializeTask(QByteArray *pBA, FreyaData pData, bool bIsSerialize)
    :QObject(NULL), QRunnable(),
    m_pBA(pBA), m_pData(pData), m_bIsSerialize(bIsSerialize)
{
    setAutoDelete(true);
}

void SerializeTask::run()
{
    if(m_pData.isNull())
    {
        emit toTaskFinished(m_pBA, m_pData, false);
        return;
    }
    if(m_bIsSerialize)
    {
        *m_pBA = m_pData->Serialize();
        emit toTaskFinished(m_pBA, m_pData, true);
    }
    else
    {
        bool bRet = m_pData->Unserialize(*m_pBA);
        emit toTaskFinished(m_pBA, m_pData, bRet);
    }
}
